#include "quick_align.hpp"

#include "qbe/src/ecs/app.hpp"

#include "qbe/src/ecs/query.hpp"

#include "qbe/src/axis/axis.hpp"
#include "qbe/src/input/input.hpp"
#include "qbe/src/select/select.hpp"
#include "qbe/src/state/state.hpp"
#include "qbe/src/undoRedo/undoRedo.hpp"
#include "qbe/src/utils/utils.hpp"

#include "qbe/src/layout/layout.hpp"
#include "qbe/src/select/select_cmd.hpp"

#include "qlib/all.h"

#include "../../select/select_cmd.hpp"

#include "../../select/select_cmd.hpp"

#include "components.hpp"
#include "events.hpp"

#include "../stretch/stretch.h"

namespace qbe::quickAlign {

const double torleance = 0.000001;

void clear_tag(ecs::Query query) {
  auto& move_param = query.get_resource<Param>();

  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;

  auto view = reg.view<QuickAnlignTag>();

  view.each([&reg](entt::entity e) { reg.remove<QuickAnlignTag>(e); });
}

void clear_events(ecs::Query query) {
  // query.get_resource<ecs::Event<Event>>().clear();
}

// 退出移动模式时回调
void exit_callback(ecs::Query query, state::AllModeType mode) {
  // 获取移动参数
  auto& move_param = query.get_resource<Param>();

  // 判断移动模式是否在执行
  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();
  auto event_writer = query.get_event_writer<Event>();

  // 获取reg
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;

  clear_tag(query);

  auto& state = query.get_resource<state::State>();
  // state.switchMode(state::SelectModeType::Overall);

  move_param.reset();
  auto& events_writer = query.get_event_reader<Event>();
  event_writer.send({Code::End});

  move_param.exit_total(query);

  state.switchMode(state::EditingChildMode::None);
  state.setAdsorption(false, true);
}

void init(ecs::Commands commands, ecs::Query query) {
  Param param;
  commands.insert_resource(param);

  auto& state = query.get_resource<state::State>();
  state.addExitCall(state::EditingModeType::QuickAlign, exit_callback);
}

// 退出模式
void exit(ecs::Query query) {
  // 获取移动参数
  auto& move_param = query.get_resource<Param>();

  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();
  auto event_writer = query.get_event_writer<Event>();

  // 获取reg
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;

  clear_tag(query);

  // 退出模式
  auto& state = query.get_resource<state::State>();
  // state.switchMode(state::SelectModeType::Overall);
  state.exit();

  auto& partSelectMode = query.get_resource<select::SelectPartModel>();
  partSelectMode.hovers.clear();
  partSelectMode.selects.clear();

  auto& sele_event_writer = query.get_event_writer<select::Event>();
  sele_event_writer.send({select::Code::PartHoverCancel});
  sele_event_writer.send({select::Code::PartSelectCancel});

  move_param.reset();
  auto& events_writer = query.get_event_reader<Event>();
  event_writer.send({Code::End});
  state.switchMode(state::EditingChildMode::None);

  move_param.exit_total(query);
  state.setAdsorption(false, true);
}

// 帧开始，模式转换
void stateTransformBegin(Param& param, state::State& state, input::HandleBundle* handle, ecs::Bus& bus) {
  // 快捷键
  input::KeyBoardShortcuts kbs;
  kbs.keyBase = vsg::KEY_q;
  kbs.keyModifier = vsg::MODKEY_Shift;

  // 如果不处于等待模式，则按下必然弹窗
  if (param.state != State::Wait && handle->keyReleased(kbs)) {
    // 按下则发送事件
    bus.send(qbe::UiMessage::OpenQuickAlignUi());

    // 等待ui消息
    param.state = State::Wait;
    return;
  }

  if (param.state == State::Wait) {
    // 尝试接收消息
    auto ok_mes = bus.receive<qbe::UiMessage::QuickAlignOk>();
    auto cancel_mes = bus.receive<qbe::UiMessage::QuickAlignCancel>();

    if (!ok_mes.empty()) {
      param.dis = qlib::Vec2d(ok_mes.back().x, ok_mes.back().y);
      return;
    }

    if (!cancel_mes.empty()) {
      param.state = State::None;
      return;
    }
  }

  if (param.state != State::Wait && handle->keyReleased(vsg::KEY_Escape)) {
    param.state = State::Cancel;
    return;
  }
}

// 帧尾-退出模式
void stateTransformEnd(State& state) {}

qlib::Vec2d projectPointOntoLine(const qlib::Vec2d& A, const qlib::Vec2d& B, const qlib::Vec2d& C) {
  // 计算向量AB和AC
  qlib::Vec2d AB = B - A;
  qlib::Vec2d AC = C - A;

  // 计算AB的长度平方（为了避免开方运算，我们可以直接使用长度平方）
  double AB_squared_length = AB.dot(AB);

  // 如果AB的长度为零（即A和B重合），则无法定义投影，这里假设A和B不会重合
  if (AB_squared_length == 0.0) {
    throw std::runtime_error("A and B are the same point, cannot project onto line AB.");
  }

  // 计算投影系数t（t = (AC · AB) / (AB · AB)）
  double t = AC.dot(AB) / AB_squared_length;

  // 计算投影点P（P = A + t * AB）
  qlib::Vec2d P = A + t * AB;

  return P;
}

// 计算位移向量（从C到投影点P）
qlib::Vec2d displacementVector(const qlib::Vec2d& C, const qlib::Vec2d& P) {
  return P - C;
}

std::vector<uint32_t> getMapChange(std::map<uint32_t, bool>& newMap, std::map<uint32_t, bool>& oldMap) {
  std::vector<uint32_t> res;

  auto it = newMap.begin();

  while (it != newMap.end()) {
    auto it_t = oldMap.find(it->first);

    if (it_t == oldMap.end() && it->second) {
      res.push_back(it->first);
    } else {
      if (it->second && !it_t->second) {
        res.push_back(it->first);
      }
    }

    it++;
  }

  return res;
}

std::vector<qlib::Vec2d*> getNewSelect(std::unordered_map<entt::entity, select::SelectPartData>& newMap,
                                       std::unordered_map<entt::entity, select::SelectPartData>& oldMap) {
  std::vector<qlib::Vec2d*> res;

  auto it = newMap.begin();
  while (it != newMap.end()) {
    auto e = it->first;
    auto& newPart = it->second;

    select::SelectPartData oldPart;

    auto it_t = oldMap.find(e);
    if (it_t == oldMap.end()) {
    } else {
      oldPart = it_t->second;
    }

    if (newPart != oldPart) {
      if (newPart.point_id_map != oldPart.point_id_map) {
        std::vector<uint32_t> ids = getMapChange(newPart.point_id_map, oldPart.point_id_map);
        if (ids.size() != 1) {
          return res;
        }
        res = {&newPart.points[ids[0]]};
      } else if (newPart.edge_id_map != oldPart.edge_id_map) {
        std::vector<uint32_t> ids = getMapChange(newPart.edge_id_map, oldPart.edge_id_map);
        if (ids.size() != 1) {
          return res;
        }
        res = {&newPart.points[ids[0]], &newPart.points[((ids[0] + 1) % newPart.points.size())]};
      } else {
        assert(0 && "既不是多了点，也不是多了边");
      }
    }

    it++;
  }

  return res;
}

std::vector<qlib::Vec2d> getChuiZhiLine(const std::vector<qlib::Vec2d>& line) {
  if (line.size() != 2) {
    return {};
  }

  const qlib::Vec2d& p1 = line[0];
  const qlib::Vec2d& p2 = line[1];

  // 计算中点
  qlib::Vec2d midpoint = (p1 + p2) * 0.5;

  // 计算原始直线的方向向量
  qlib::Vec2d direction = p2 - p1;

  // 计算垂直方向向量（旋转90度）
  qlib::Vec2d perpendicular_direction(-direction.y, direction.x);

  // 归一化垂直向量（手动归一化，避免 normalize() 可能的问题）
  double perpendicular_length = direction.length();  // AB的长度
  if (perpendicular_length == 0) {
    return {};  // 避免除以零
  }

  // 归一化垂直向量
  qlib::Vec2d perpendicular_normalized = perpendicular_direction / perpendicular_length;

  // 计算垂直直线的两个端点（从 midpoint 向两侧延伸 AB 长度的一半）
  qlib::Vec2d end1 = midpoint + perpendicular_normalized * (perpendicular_length / 2);
  qlib::Vec2d end2 = midpoint - perpendicular_normalized * (perpendicular_length / 2);

  return {end1, end2};
}

bool haveSelectObj(ecs::Query query, std::vector<qlib::Vec2d>& res_t) {
  auto& axisModule = query.get_resource<axis::Axis>();
  auto& select_cmd = query.get_resource<select::SelectCmd>();
  auto& models = query.get_resource<Param>();

  std::unordered_map<entt::entity, select::SelectPartData> partMap;

  select_cmd.query_nodes(axisModule.real_x, axisModule.real_y);
  auto value = select_cmd.get_part(axisModule.real_x, axisModule.real_y, partMap, true, models.isPointAlign, !models.isPointAlign);

  std::vector<qlib::Vec2d*> res = getNewSelect(partMap, models.partMap);
  if (!res.empty()) {
    bool is_point = res.size() == 1;

    if (is_point == models.isPointAlign) {
      if (models.isPointAlign) {
        select_cmd.algin_add_select_parts(partMap);
        models.partMap = partMap;
        res_t = {*res[0]};
        return true;
      } else {
        if (!models.haveOneLine) {
          select_cmd.algin_add_select_parts(partMap);
          models.partMap = partMap;
          res_t = {*res[0], *res[1]};
          return true;
        } else {
          qlib::Vec2dArray points = {*(res[0]), *(res[1])};
          if (!models.isPointAlign) {
            bool is_chuizhi = false;
            double k = 0;

            if (points[1].x == points[0].x) {
              is_chuizhi = true;
            } else {
              k = (points[1].y - points[0].y) / (points[1].x - points[0].x);
            }

            if (models.dx_eaual_0) {
              if (is_chuizhi) {
                select_cmd.algin_add_select_parts(partMap);
                res_t = {*res[0], *res[1]};
                return true;
              } else if (k == 0) {
                double center_x = (points[1].x + points[0].x) / 2.0;
                double center_y = (points[1].y + points[0].y) / 2.0;
                double half_width = (points[1] - points[0]).length() / 2.0;

                *(res[0]) = qlib::Vec2d(center_x, center_y - half_width);
                *(res[1]) = qlib::Vec2d(center_x, center_y + half_width);

                select_cmd.algin_add_select_parts(partMap);
                res_t = {*res[0], *res[1]};
                return true;
              }
            } else {
              if (models.k == 0 && is_chuizhi) {
                double length = (points[0] - points[1]).length();
                double x = points[0].x;
                double y = (points[0].y + points[1].y) / 2.0;

                *(res[0]) = qlib::Vec2d(x - length / 2.0, y);
                *(res[1]) = qlib::Vec2d(x + length / 2.0, y);

                select_cmd.algin_add_select_parts(partMap);
                res_t = {*res[0], *res[1]};
                return true;
              } else if (!is_chuizhi) {
                if (std::abs(models.k - k) < torleance) {
                  select_cmd.algin_add_select_parts(partMap);
                  res_t = {*res[0], *res[1]};
                  return true;
                } else if (std::abs(models.k * k + 1) < torleance) {
                  auto line = getChuiZhiLine(points);

                  *(res[0]) = line[0];
                  *(res[1]) = line[1];

                  select_cmd.algin_add_select_parts(partMap);
                  res_t = {*res[0], *res[1]};
                  return true;
                }
              }
            }
          }
        }
      }
    }
  }

  return false;
}

void buildStretchMoldes(ecs::Query query) {
  auto& models = query.get_resource<Param>();
  auto& stretchModels = query.get_resource<Stretch::StretchModel>();
  auto& select_cmd = query.get_resource<select::SelectCmd>();

  stretchModels.selects.clear();
  stretchModels.sel_entity_all.clear();
  auto& selects = select_cmd.select_parts();
  stretchModels.selects = Stretch::partSelectToStretch(query, selects);

  stretchModels.ele_in_selected_points.clear();
  for (auto& sel : stretchModels.selects) {
    auto iter_ele = stretchModels.ele_in_selected_points.find(sel.select_entity);
    if (iter_ele == stretchModels.ele_in_selected_points.end()) {
      auto info = std::make_shared<Stretch::StretchInfo>();
      info->key_points = sel.key_points;
      for (auto i : sel.select_indexs)
        info->select_indexs.insert(i);
      stretchModels.ele_in_selected_points.insert({sel.select_entity, info});
    } else {
      for (auto i : sel.select_indexs)
        iter_ele->second->select_indexs.insert(i);
    }
  }
}

void StretchUndoRedo(ecs::Query query) {
  auto& stretchModels = query.get_resource<Stretch::StretchModel>();
  auto& undoRedoModule = query.get_resource<undoRedoT::UndoRedoModule>();
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  undoRedoModule.start(cell);
  for (auto i : stretchModels.ele_in_selected_points) {
    undoRedoModule.change(i.first);
  }
}

void StretchUpdate(ecs::Query query) {
  auto& stretchModels = query.get_resource<Stretch::StretchModel>();
  auto& undoRedoModule = query.get_resource<undoRedoT::UndoRedoModule>();
  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();

  auto& cell_reg = cell->reg;
  for (auto& sel : stretchModels.ele_in_selected_points) {
    {
      auto _ptr = cell_reg.try_get<qlib::Polygon::s_ptr>(sel.first);
      if (_ptr) {
        auto poly = *_ptr;
        poly->points = sel.second->key_points;
      }
    }
    {
      auto _ptr = cell_reg.try_get<qlib::ViewArea3D::s_ptr>(sel.first);
      if (_ptr) {
        auto poly = *_ptr;
        poly->points = sel.second->key_points;
      }
    }
    {
      auto _ptr = cell_reg.try_get<qlib::Ellipse::s_ptr>(sel.first);
      if (_ptr) {
        auto elli = *_ptr;
        std::vector<qlib::Vec2d> points = sel.second->key_points;
        qlib::Vec2d center = (points[2] + points[0]) / 2;

        qlib::Vec2d width_height;
        width_height.x = (points[1] - points[0]).length();
        width_height.y = (points[3] - points[0]).length();

        elli->center = center;
        elli->radius = width_height / 2;
      }
    }
    {
      auto _ptr = cell_reg.try_get<qlib::Rectangle::s_ptr>(sel.first);
      if (_ptr) {
        auto rec = *_ptr;
        std::vector<qlib::Vec2d> points = sel.second->key_points;

        qlib::Vec2d width_height;
        width_height.x = (points[1] - points[0]).length();
        width_height.y = (points[3] - points[0]).length();

        qlib::Vec2d center = (points[2] + points[0]) / 2;
        rec->width = width_height.x;
        rec->height = width_height.y;
        rec->pos = center - qlib::Vec2d(width_height.x / 2.0, width_height.y / 2.0);
      }
    }

    {
      auto _ptr = cell_reg.try_get<qlib::SectionArea3D::s_ptr>(sel.first);
      if (_ptr) {
        auto rec = *_ptr;
        std::vector<qlib::Vec2d> points = sel.second->key_points;

        qlib::Vec2d width_height;
        width_height.x = (points[1] - points[0]).length();
        width_height.y = (points[3] - points[0]).length();

        qlib::Vec2d center = (points[2] + points[0]) / 2;
        rec->width = width_height.x;
        rec->height = width_height.y;
        rec->pos = center - qlib::Vec2d(width_height.x / 2.0, width_height.y / 2.0);
      }
    }

    {
      auto _ptr = cell_reg.try_get<qlib::Fan::s_ptr>(sel.first);
      if (_ptr) {
        auto fan = *_ptr;
        auto iter = stretchModels.fan_temp_fans.find(sel.first);
        if (iter == stretchModels.fan_temp_fans.end()) {
        } else {
          *fan = *(iter->second);
        }
      }
    }

    {
      auto _ptr = cell_reg.try_get<qlib::Path::s_ptr>(sel.first);
      if (_ptr) {
        auto line = *_ptr;
        std::vector<qlib::Vec2d> points = sel.second->key_points;

        line->spine.points = points;
      }
    }

    {
      auto _ptr = cell_reg.try_get<qlib::Ruler::s_ptr>(sel.first);
      if (_ptr) {
        auto ruler = *_ptr;
        std::vector<qlib::Vec2d> points = sel.second->key_points;

        ruler->start = points[0];
        ruler->end = points[1];
      }
    }
  }
  for (auto i : stretchModels.selects)
    layout_cmd.update(i.select_entity);
  undoRedoModule.end();
}

// 移动逻辑
void update(ecs::Query query, ecs::Bus& bus) {
  // 判断编辑模式为移动
  auto& state = query.get_resource<state::State>();

  if (state.getEditingMode() == state::EditingModeType::None && state.getDrawingMode() == state::DrawingModeType::None) {
    if (state.getSelectMode() == state::SelectModeType::None) {
      // state.switchMode(state::SelectModeType::Overall);
    }
  }

  if (state.getEditingMode() != state::EditingModeType::QuickAlign) {
    return;
  }

  auto& select_models = query.get_resource<select::OverallModel>();
  // select_models.mouse_press = false;
  // select_models.mouse_release = false;

  // 获取cell和reg
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;
  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();

  // 获取输入 坐标系 撤销恢复
  auto& handModule = query.get_resource<vsg::ref_ptr<input::HandleBundle>>();
  auto& axisModule = query.get_resource<axis::Axis>();
  auto& undoRedoModule = query.get_resource<undoRedoT::UndoRedoModule>();

  // 获取移动参数
  auto& param = query.get_resource<Param>();
  auto event_writer = query.get_event_writer<Event>();

  if (handModule->keyReleased(vsg::KEY_Space) || param.need_change) {
    bool isPointAlign = !param.isPointAlign;

    clear_tag(query);
    param.reset();
    state.exit();
    param.exit_total(query);

    auto& sele_event_writer = query.get_event_writer<select::Event>();
    sele_event_writer.send({select::Code::SelectUpdate});
    sele_event_writer.send({select::Code::HoverUpdate});

    state.switchMode(state::EditingModeType::QuickAlign);
    param.isPointAlign = isPointAlign;
    param.need_change = false;
    return;
  }

  stateTransformBegin(param, state, handModule, bus);

  if (!param.use_default_quick_align) {
    param.last_is_point = param.isPointAlign;
  }

  if (!param.tag_select_obj) {
    param.tag_select_obj = true;

    auto view = reg.view<select::SelectTag>();

    view.each([&reg](entt::entity e) {
      reg.emplace_or_replace<QuickAnlignTag>(e);
      e = e;
    });
  }

  // state.switchMode(state::SelectModeType::Part);
  if (!state.isAdsorption() && param.quick_first) {
    state.setAdsorption(true, false);
  }

  auto& events = query.get_event_reader<select::Event>();
  auto& selectModels = query.get_resource<select::SelectPartModel>();

  bool is_select_point = false;
  qlib::Vec2d select_point;

  bool is_select_line = false;
  qlib::Vec2dArray select_line;

  auto& select_cmd = query.get_resource<select::SelectCmd>();

  select_cmd.clear_hover_parts();

  if (handModule->mousePressed(vsg::ButtonMask::BUTTON_MASK_1)) {
    std::vector<qlib::Vec2d> res;
    bool value = haveSelectObj(query, res);
    if (value) {
      if (res.size() > 1) {
        is_select_line = true;
        select_line = res;
      } else {
        is_select_point = true;
        select_point = res[0];
      }
    }
  } else {
    select_cmd.clear_hover_parts();
    std::unordered_map<entt::entity, select::SelectPartData> partMap;

    select_cmd.get_part(axisModule.real_x, axisModule.real_y, partMap, true, param.isPointAlign, !param.isPointAlign);
    if (!partMap.empty()) {
      auto it = partMap.begin();
      auto& data = it->second;
      qlib::Vec2dArray points;

      if (!data.point_id_map.empty()) {
        auto id = data.point_id_map.begin()->first;
        points = {data.points[id]};

        if (param.isPointAlign) {
          select_cmd.add_hover_parts(partMap);
        }
      } else if (!data.edge_id_map.empty()) {
        if (!param.haveOneLine) {
          auto id = data.edge_id_map.begin()->first;
          points = {data.points[id], data.points[((id + 1) % data.points.size())]};

          if (!param.isPointAlign) {
            select_cmd.add_hover_parts(partMap);
          }
        } else {
          auto id = data.edge_id_map.begin()->first;
          points = {data.points[id], data.points[((id + 1) % data.points.size())]};

          if (!param.isPointAlign) {
            bool is_chuizhi = false;
            double k = 0;

            if (points[1].x == points[0].x) {
              is_chuizhi = true;
            } else {
              k = (points[1].y - points[0].y) / (points[1].x - points[0].x);
            }

            if (param.dx_eaual_0) {
              if (is_chuizhi) {
                select_cmd.add_hover_parts(partMap);
              } else if (k == 0) {
                double center_x = (points[1].x + points[0].x) / 2.0;
                double center_y = (points[1].y + points[0].y) / 2.0;
                double half_width = (points[1] - points[0]).length() / 2.0;

                data.points[id] = qlib::Vec2d(center_x, center_y - half_width);
                data.points[((id + 1) % data.points.size())] = qlib::Vec2d(center_x, center_y + half_width);

                select_cmd.add_hover_parts(partMap);
              }
            } else {
              if (param.k == 0 && is_chuizhi) {
                double length = (points[0] - points[1]).length();
                double x = points[0].x;
                double y = (points[0].y + points[1].y) / 2.0;

                data.points[id] = qlib::Vec2d(x - length / 2.0, y);
                data.points[((id + 1) % data.points.size())] = qlib::Vec2d(x + length / 2.0, y);

                select_cmd.add_hover_parts(partMap);
              } else if (!is_chuizhi) {
                if (std::abs(param.k - k) < torleance) {
                  select_cmd.add_hover_parts(partMap);
                } else if (std::abs(param.k * k + 1) < torleance) {
                  auto line = getChuiZhiLine(points);

                  data.points[id] = line[0];
                  data.points[((id + 1) % data.points.size())] = line[1];

                  select_cmd.add_hover_parts(partMap);
                }
              }
            }
          }
        }

      } else {
      }
    }
  }

  bool haveSelectObj = false;
  auto view = reg.view<select::SelectTag>();
  view.each([&haveSelectObj](entt::entity) { haveSelectObj = true; });

  bool havePartObj = false;
  std::unordered_map<entt::entity, select::SelectPartData> partMap;
  havePartObj = select_cmd.get_part(axisModule.real_x, axisModule.real_y, partMap, true, param.isPointAlign, !param.isPointAlign);

  if (!param.haveOnePoint && !param.haveOneLine && param.quick_first) {
    if (haveSelectObj && !havePartObj) {
      param.isStretch = false;
    } else {
      param.isStretch = true;
      std::unordered_map<entt::entity, select::SelectPartData> partMap;
      select_cmd.get_part(axisModule.real_x, axisModule.real_y, partMap, true, param.isPointAlign, !param.isPointAlign);

      param.stretchPartMap = partMap;

      if (param.stretchPartMap.empty()) {
        param.state = State::Cancel;
      }
    }
  }

  if (haveSelectObj == false && havePartObj == false) {
    param.state = State::Cancel;
  }

  param.quick_first = false;

  if (param.isPointAlign) {
    auto& events_writer = query.get_event_reader<Event>();
    event_writer.send({Code::BeginPoint});
    state.switchMode(state::EditingChildMode::QuickPointAlign);
  }

  if (!param.isPointAlign) {
    auto& events_writer = query.get_event_reader<Event>();
    event_writer.send({Code::BeginLine});
    state.switchMode(state::EditingChildMode::QuickLineAlign);
  }

  if (is_select_point && param.isPointAlign) {
    if (!param.haveOnePoint) {
      param.pos = select_point;
      param.haveOnePoint = true;
    } else {
      qlib::Vec2d translate = select_point - param.pos;
      translate += param.dis;

      if (!param.isStretch) {
        auto view = reg.view<QuickAnlignTag>();

        undoRedoModule.start(cell);

        view.each([&reg, &undoRedoModule, &translate, &layout_cmd](entt::entity e) {
          undoRedoModule.change(e);

          utils::translate(reg, e, translate);

          layout_cmd.update(e);
        });

        undoRedoModule.end();

      } else {
        auto& stretch = query.get_resource<Stretch::StretchModel>();

        buildStretchMoldes(query);

        StretchUndoRedo(query);

        Stretch::stretch(query, translate.x, translate.y);

        StretchUpdate(query);
      }

      clear_tag(query);
      param.reset();
      state.exit();

      param.exit_total(query);

      auto& events_writer = query.get_event_reader<Event>();
      event_writer.send({Code::End});

      state.switchMode(state::EditingChildMode::None);

      auto& partSelectMode = query.get_resource<select::SelectPartModel>();
      partSelectMode.hovers.clear();
      partSelectMode.selects.clear();

      auto& sele_event_writer = query.get_event_writer<select::Event>();
      sele_event_writer.send({select::Code::SelectUpdate});
      sele_event_writer.send({select::Code::HoverUpdate});
    }
  }

  if (is_select_line && !param.isPointAlign) {
    if (!param.haveOneLine) {
      param.line = select_line;
      param.haveOneLine = true;

      double dy = (param.line.back().y - param.line.front().y);
      double dx = (param.line.back().x - param.line.front().x);

      if (dx != 0) {
        param.k = (dy / dx);
        param.dx_eaual_0 = false;
      } else
        param.dx_eaual_0 = true;

      param.length = (param.line.back() - param.line.front()).length();
    } else {
      double dy = (select_line.back().y - select_line.front().y);
      double dx = (select_line.back().x - select_line.front().x);

      bool success = false;
      double k = 0;

      if (dx == 0) {
        if (param.dx_eaual_0)
          success = true;
      } else {
        k = dy / dx;
        if (std::abs(k - param.k) <= torleance)
          success = true;
      }

      if (success) {
        qlib::Vec2d translate;

        if (param.dx_eaual_0) {
          translate.x = select_line.front().x - param.line.front().x;
        } else if (k == 0) {
          translate.y = select_line.front().y - param.line.front().y;
        } else {
          // double b1 = select_line.front().y - k * select_line.front().x;
          // double b2 = param.line.front().y - param.k * param.line.front().x;

          // translate = qlib::Vec2d(b2 - b1, b2 - b1);

          auto p = param.line.front();
          auto p1 = select_line.front();
          auto p2 = select_line.back();

          translate = -displacementVector(projectPointOntoLine(p1, p2, p), p);
        }

        translate += param.dis;
        // translate.x += (translate.x >= 0 ? -param.dis.x : param.dis.x);
        // translate.y += (translate.y >= 0 ? -param.dis.y : param.dis.y);

        if (!param.isStretch) {
          auto view = reg.view<QuickAnlignTag>();

          undoRedoModule.start(cell);

          view.each([&reg, &undoRedoModule, &translate, &layout_cmd](entt::entity e) {
            undoRedoModule.change(e);

            utils::translate(reg, e, translate);

            layout_cmd.update(e);
          });

          undoRedoModule.end();
        } else {
          auto& stretch = query.get_resource<Stretch::StretchModel>();

          buildStretchMoldes(query);

          StretchUndoRedo(query);

          Stretch::stretch(query, translate.x, translate.y);

          StretchUpdate(query);
        }

        clear_tag(query);
        param.reset();
        state.exit();

        param.exit_total(query);

        auto& sele_event_writer = query.get_event_writer<select::Event>();
        sele_event_writer.send({select::Code::SelectUpdate});
        sele_event_writer.send({select::Code::HoverUpdate});
      }
    }
  }

  if (param.state == State::Cancel) {
    clear_tag(query);
    param.reset();
    state.exit();

    // auto& partSelectMode = query.get_resource<select::SelectPartModel>();
    // partSelectMode.hovers.clear();
    // partSelectMode.selects.clear();

    param.exit_total(query);

    auto& sele_event_writer = query.get_event_writer<select::Event>();
    sele_event_writer.send({select::Code::SelectUpdate});
    sele_event_writer.send({select::Code::HoverUpdate});
  }

  stateTransformEnd(param.state);
}

void Plugin::build(ecs::App& app) {
  app.add_stage<InitStage>();
  app.add_stage<UpdateStage>();

  app.init_event<Event>();

  app.add_systems<InitStage>(init);
  app.add_systems<UpdateStage>(update);
  app.add_systems<ecs::stage::Last>(clear_events);
}

}  // namespace qbe::quickAlign